package com.xiaojie.parse;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.xiaojie.exception.ConfigFileCircleException;
import com.xiaojie.parse.model.DataModel;
import com.xiaojie.parse.model.RefTable;
import com.xiaojie.parse.model.Settings;
import com.xiaojie.parse.model.Table;
import com.xiaojie.progress.ExportStage;
import com.xiaojie.toolkits.FieldNameUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.security.InvalidParameterException;
import java.util.*;
import java.util.function.Function;

/**
 * 解析配置文件转换成对象
 *
 * @author wangye
 * @classname DataModelParse
 * @date 2021/6/25 14:29
 **/
@Slf4j
@Component
public class DataModelParse {

    /**
     * 获取配置文件内容
     *
     * @param configFilePath
     * @auther wangye
     * @date 2021/6/25
     */
    public static DataModel getDataModel(Function<String, String> custGetFileContent, String configFilePath, Boolean isExport) throws Exception {
        ExportStage.LOAD_CONFIG.progress(0);
        if (custGetFileContent == null) {
            custGetFileContent = filePath -> getFileContent(filePath);
        }
        DataModel dataModel = convertToDataModel(custGetFileContent, configFilePath, new HashSet<>());
        LinkedHashMap<String, Table> tables = dataModel.getTables();
        dataModel.setRefFieldsMap(getRefFieldsMap(tables, dataModel.getSettings()));
        if (Boolean.TRUE.equals(isExport)) {
            //导出,进行数据处理
            doExportPreHandle(dataModel);
        }
        ExportStage.LOAD_CONFIG.progress(100);

        //全局setting与表级setting合并
        dataModel.mergeSetting();

        return dataModel;
    }


    /**
     * 多个配置文件合并,父级配置覆盖子级配置
     *
     * @param configFilePath
     * @Author: wangye
     * @Date: 2023/9/12 15:37
     * @return:
     **/
    private static DataModel convertToDataModel(String configFilePath, Set<String> parsedConfigFilePth) throws Exception {
        if (!parsedConfigFilePth.add(configFilePath)) {
            throw new ConfigFileCircleException(String.format("%s文件循环依赖", configFilePath));
        }
        String content = getFileContent(configFilePath);
        DataModel dataModel = JSON.parseObject(content, DataModel.class, Feature.OrderedField);
        for (String childFilePath : dataModel.getRefFilePaths()) {
            DataModel childDataModel = convertToDataModel(childFilePath, parsedConfigFilePth);
            mergeTables(dataModel, childDataModel);
        }
        return dataModel;
    }

    private static DataModel convertToDataModel(Function<String, String> custGetFileContent, String configFilePath, Set<String> parsedConfigFilePth) throws Exception {
        if (!parsedConfigFilePth.add(configFilePath)) {
            throw new ConfigFileCircleException(String.format("%s文件循环依赖", configFilePath));
        }
        String content = custGetFileContent.apply(configFilePath);
        DataModel dataModel = JSON.parseObject(content, DataModel.class, Feature.OrderedField);
        for (String childFilePath : dataModel.getRefFilePaths()) {
            DataModel childDataModel = convertToDataModel(custGetFileContent, childFilePath, parsedConfigFilePth);
            mergeTables(dataModel, childDataModel);
        }
        return dataModel;
    }

    private static String getFileContent(String filePath) {
        try {
            if (StringUtils.isBlank(filePath)) {
                throw new InvalidParameterException("filePath is empty");
            }
            ClassPathResource classPathResource = new ClassPathResource(filePath);
            InputStream is = classPathResource.getInputStream();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int i;
            while ((i = is.read()) != -1) {
                baos.write(i);
            }
            return baos.toString();

        } catch (Exception e) {
            log.error("导入导出获取配置文件内容失败:", e);
        }
        return null;
    }


    /**
     * 多个文件表配置合并,相同表配置父级配置覆盖子级配置
     *
     * @param parent
     * @param child
     * @Author: wangye
     * @Date: 2023/9/12 15:34
     * @return:
     **/
    private static void mergeTables(DataModel parent, DataModel child) {
        child.getTables().entrySet().forEach(entry -> {
            LinkedHashMap<String, Table> tableMap = parent.getTables();
            String tableName = entry.getKey();
            if (!tableMap.containsKey(tableName)) {
                tableMap.put(tableName, entry.getValue());
            }
        });

    }

    private static void doExportPreHandle(DataModel dataModel) {
        //导出时，表关联关系进行处理，导出不按正确关联顺序
        LinkedHashMap<String, Table> tables = dataModel.getTables();
        for (String tableName : tables.keySet()) {
            Table table = tables.get(tableName);
            List<RefTable> refTables = table.getRefTables();
            String tempS;
            if (refTables != null) {
                for (Iterator<RefTable> iterator = refTables.iterator(); iterator.hasNext(); ) {
                    RefTable rt = iterator.next();
                    if (Boolean.FALSE.equals(rt.getSelect())) {
                        //直接删除
                        iterator.remove();
                    } else {
                        if (Boolean.FALSE.equals(rt.getForward())) {
                            //需要反向关联
                            Table rtRt = tables.get(rt.getTableName());
                            //反向关联操作
                            rt.setForward(Boolean.TRUE);
                            tempS = rt.getSrcField();
                            rt.setSrcField(rt.getRefField());
                            rt.setRefField(tempS);
                            rt.setTableName(tableName);
                            List<RefTable> refTables1 = rtRt.getRefTables();
                            if (refTables1 == null) {
                                refTables1 = new ArrayList<>();
                                rtRt.setRefTables(refTables1);
                            }
                            refTables1.add(rt);
                            iterator.remove();
                        }
                    }
                }
            }
        }
    }

    /**
     * 获取表被关联的字段
     *
     * @param tableMap
     * @auther wangye
     * @date 2021/8/7
     */
    private static Map<String, Set<String>> getRefFieldsMap(Map<String, Table> tableMap, Settings settings) {
        Map<String, Set<String>> refFieldsMap = Maps.newHashMap();
        tableMap.forEach((key, value) -> {
            if (value.getRefTables() != null) {
                value.getRefTables().forEach(refTable -> {
                    String refTableName = refTable.getTableName();
                    Set<String> filedNameSet = refFieldsMap.get(refTableName);
                    filedNameSet = filedNameSet == null ? Sets.newHashSet() : filedNameSet;
                    filedNameSet.add(FieldNameUtil.convert(refTable.getRefField(), settings));
                    refFieldsMap.put(refTableName, filedNameSet);
                });
            }
        });
        return refFieldsMap;
    }


}
